﻿/*SOWY DateOperations class
 * Author:
 * Group: f10-2s
 * Version 0.01
 * This class is to process information received from the keywordProcessor class.
 * In this class, we know which tasks (CRUD) to do from the keywords received and also the type of tasks, (floating etc...)
 * However, this task mainly deal with date time 
 * e.g. of data received can be "ADD task1 during DateTime" This class would require to figure out which date the date is referring to.
 * Deal with CRUD keywords (Easy) and task type.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SOWY.common;

namespace SOWY
{
    class DataProcessing
    {
        private DateTime deadline;
        private DateTime startDate;
        private string crudType;
        private string dateTypeKeywords;
        private string task;
        private string date;
        private string taskStatus;
        private string[] tasks;
        private string[] dates;
        private List<Command> comd;

        public DataProcessing()
        {
            this.crudType = "";
            this.task = "";
            this.dateTypeKeywords = "";
            this.date = "";
        }
        public DataProcessing(string crudType, string task, string dateTypeKeywords, string date)
        {
            this.crudType = crudType.ToLower();
            this.task = task;
            this.dateTypeKeywords = dateTypeKeywords.ToLower();
            this.date = date;
        }
        public DataProcessing(string crudType, string task, string dateTypeKeywords, string date, string taskStatus)
        {
            this.crudType = crudType.ToLower();
            this.task = task;
            this.dateTypeKeywords = dateTypeKeywords.ToLower();
            this.date = date;
            this.taskStatus = taskStatus;
        }

        public List<Command> processData(string crudType, string task, string dateTypeKeywords, string date)
        {
            comd = new List<Command>();
            initAllVariables(crudType, task, dateTypeKeywords, date);

            try
            {
                processAll();
                return comd;
            }
            catch (Exception e)
            {
                List<Command> errorCommand = new List<Command>();
                errorCommand.Add(new Command(GeneralConstant.ERROR_INVALID_DATE, GeneralConstant.ERROR, DateTime.MinValue, DateTime.MinValue, null, null));
                return errorCommand;
            }
        }

        private void initAllVariables(string crudType, string task, string dateTypeKeywords, string date)
        {
            this.crudType = crudType.ToLower();
            this.task = task;
            this.dateTypeKeywords = dateTypeKeywords.ToLower();
            this.date = date;

            task = task.Replace(" to ", "~");
            string[] temp = task.Split('~');

            tasks = temp[0].Split(GeneralConstant.DELIMITER_AGENDA);
            foreach (string s in tasks)
                s.Trim();

            dates = date.Split(GeneralConstant.DELIMITER_AGENDA);
            foreach (string s in dates)
                s.Trim();
        }

        private bool processAll()
        {
            for (int i = 0; i < tasks.Length; i++)
            {
                for (int j = 0; j < dates.Length; j++)
                {
                    date = dates[j];
                    if (crudType.CompareTo(GeneralConstant.KEY_UPDATE) == 0)
                    {
                        if (dateTypeKeywords.CompareTo(GeneralConstant.KEY_FOR) == 0)
                        {
                            processUpdateDate(i);
                        }
                        else
                        {
                            processUpdateTask(i);
                        }    
                    }
                    else if (processTaskType(date))
                    {
                        if (crudType.CompareTo(GeneralConstant.KEY_MARK) == 0)
                        {
                            processMarkTask(i);
                        }
                        if (crudType.CompareTo(GeneralConstant.KEY_ADD) == 0)
                        {
                            comd.Add(new Command(tasks[i], crudType, startDate, deadline));
                        }
                        if (crudType.CompareTo(GeneralConstant.KEY_DELETE) == 0 || crudType.CompareTo(GeneralConstant.KEY_RETRIEVE)==0)
                        {
                            //need task formatted here for first 5, last 6, everything etc
                            comd.Add(new Command(tasks[i], crudType, startDate, deadline));
                        }
                    }
                    else
                    {
                        comd.Add(new Command(GeneralConstant.ERROR_INVALID_DATE, GeneralConstant.ERROR, DateTime.MinValue, DateTime.MinValue));
                    }
                }
            }
            return true;
        }
        private void processUpdateDate(int i)
        {
            TaskFormatter tf = new TaskFormatter(date, dateTypeKeywords, i);
            tf.formatTaskforUpdate();
            DateTime searchDate = processDate(tf.oldtokens[0]);
            for (int k = 0; k < tf.newValue.Length; k++)
                tf.newValue[k] = processDate(tf.newValue[k]).ToString();
            comd.Add(new Command(task, crudType, DateTime.MinValue, searchDate, tf.parameter, tf.newValue));
        }
        private void processUpdateTask(int i)
        {
            TaskFormatter tf = new TaskFormatter(task, dateTypeKeywords, i);
            tf.formatTaskforUpdate();
            processTaskType(date);
            if(tf.newValue != null)
                comd.Add(new Command(tf.oldtokens[i], crudType, startDate, deadline, tf.parameter, tf.newValue));
            else
                comd.Add(new Command(GeneralConstant.ERROR_INCORRECT_PARAMETER, GeneralConstant.ERROR, DateTime.MinValue, DateTime.MinValue, null, null));
        }
        private void processMarkTask(int i)
        {
            string[] para = { GeneralConstant.KEY_STATUS };
            string[] newValue = { taskStatus };
            comd.Add(new Command(tasks[i], GeneralConstant.KEY_UPDATE, startDate, deadline, para, newValue));
        }

        private bool processTaskType(string date)
        {
            switch (dateTypeKeywords)
            {
                case GeneralConstant.KEY_AFTER:   
                    return afterTask(date);

                case GeneralConstant.KEY_BEFORE: 
                    return beforeTask(date);

                case GeneralConstant.KEY_BETWEEN: 
                    return betweenTask(date);

                case GeneralConstant.KEY_ON:
                    return beforeTask(date);

                case GeneralConstant.KEY_FLOATING:
                    {
                        startDate = DateTime.MinValue;
                        deadline = DateTime.MinValue;
                        return true;
                    }

                default:
                    return false;
            }
        }

        private bool afterTask(string date)
        {
            startDate = processDate(date);
            startDate = startDate.AddDays(1);
            return checkDate(startDate);
        }
        private bool beforeTask(string date)
        {
            deadline = processDate(date);
            return checkDate(deadline);
        }
        private bool betweenTask(string date)
        {
            DateTime[] processedDate = processTwoDate(this.date);
            startDate = processedDate[0];
            deadline = processedDate[1];
            return (checkDate(startDate) & checkDate(deadline));
        }
        private bool checkDate(DateTime startate)
        {
            if (startate.Year == 1 || startate.Year == 2)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
 
        private DateTime[] processTwoDate(string date)
        {
            string[] unformattedTime = seperateDates(date);
            DateTime[] dateTime = new DateTime[2];

            dateTime[0] = processDate(unformattedTime[0].Trim(), "start");
            dateTime[1] = processDate(unformattedTime[1].Trim(), "end", dateTime[0]);

            if (dateTime[1] < dateTime[0])
                dateTime[1] = dateTime[1].AddYears(1);

            return dateTime;
        }
        private string[] seperateDates(string date)
        {
            string[] unformattedTime = new string[2];
            string[] delimiter = { GeneralConstant.DELIMITER_TO, GeneralConstant.DELIMITER_AND, GeneralConstant.DELIMITED_TILL };

            if (date.Length == 0)
            {
                unformattedTime[0] = "";
                unformattedTime[1] = "";
            }
            else
            {
                try
                {
                    unformattedTime = date.ToLower().Split(delimiter, StringSplitOptions.RemoveEmptyEntries);
                    if (unformattedTime.Length == 1)
                    {
                        unformattedTime = new string[2];
                        unformattedTime[0] = unformattedTime[1] = "";
                    }
                }
                catch (Exception e)
                {
                    unformattedTime[0] = unformattedTime[1] = "";
                }
            }

            return unformattedTime;
        }

        private DateTime processDate(string date)
        {
            DateProcessor dp = new DateProcessor(date, dateTypeKeywords);
            return dp.processDate();
        }
        private DateTime processDate(string date, string startEnd)
        {
            DateProcessor dp = new DateProcessor(date, dateTypeKeywords, startEnd);
            return dp.processDate();
        }
        private DateTime processDate(string date, string startEnd, DateTime compare)
        {
            DateProcessor dp = new DateProcessor(date, dateTypeKeywords, startEnd, compare);
            return dp.processDate();
        }
    }
}
